<template>
	<div class="search" ref="searchRef">
		<div class="list" ref="listRef" v-if="allSearchData?.length! > 0" :style="{ padding: `0 0 ${scroollPadding}px 0` }">
			<template v-for="(item, index) in visibleSearchData" :key="item.cfi">
				<div
					:style="itemInitialStyle"
					class="item"
					:data-index="index"
					v-html="item.excerpt"
					@click="() => handelClickSearchItem(item.cfi)"
				></div>
			</template>
		</div>
		<div v-if="isSearching" class="searching">正在为您全速搜索--------</div>
		<div v-else-if="allSearchData && allSearchData.length === 0 && !isSearching" class="searching">未找到结果</div>
	</div>
</template>

<script setup lang="ts">
import useBooks from "@/store/books";
import { storeToRefs } from "pinia/dist/pinia";
import { doSearch, getCurrentLocation } from "@/utils/bookContent";
import type { Book } from "epubjs";
import { onMounted, onUnmounted, onUpdated, ref, watch } from "vue";
import throttle from "@/utils/throttle";
import useDirectory from "@/store/directory";

const searchRef = ref<HTMLDivElement>();
const listRef = ref<HTMLDivElement>();
const bookStore = useBooks();
const directoryStore = useDirectory();
const { confirmSearch, searchValue } = storeToRefs(directoryStore);
const { bookPrototype, showDialog, currentMenu, currentLocationPercentage, totalPageLength, currentSection } =
	storeToRefs(bookStore);
const allSearchData = ref<{ excerpt: string; cfi: string }[] | undefined>(undefined);
const visibleSearchData = ref<{ excerpt: string; cfi: string }[]>([]);
const isAnalysisOver = ref(false);
const isSearching = ref(false);
const itemInitialStyle = ref({});
const Intersection = ref();
const oneGroupDataNum = ref(15);
const scroollPadding = ref(0);
// 达到最大节点数量后设置的中间那一组的第一个节点索引
const currentHeadIndex = ref(0);
// 由滚动导致加载的最大数据索引
const currentMaxIndex = ref(0);
// 控制是否执行onUpdated
let isUpdate = true;

// 修改首尾节点标记
const alterLocation = (arriveMax: boolean, isDelete: boolean) => {
	let itemHeadEle;
	let itemEndEle;
	// 因为有空文本节点因此取这样的索引;
	// 避免观察的下一个节点等于上一个观察的节点
	if (arriveMax) {
		itemHeadEle = listRef.value!.childNodes[oneGroupDataNum.value - Math.floor(oneGroupDataNum.value / 2)];
		itemEndEle = listRef.value!.childNodes[2 * oneGroupDataNum.value + Math.floor(oneGroupDataNum.value / 2)];
	} else {
		itemHeadEle = listRef.value!.childNodes[1];
		itemEndEle = listRef.value!.childNodes[visibleSearchData.value.length];
	}
	if (isDelete) {
		(itemHeadEle as Element).className = "item";
		(itemEndEle as Element).className = "item";
		Intersection.value.unobserve(itemHeadEle);
		Intersection.value.unobserve(itemEndEle);
	} else {
		(itemHeadEle as Element).className = "item itemHead";
		(itemEndEle as Element).className = "item itemEnd";
		Intersection.value.observe(itemHeadEle);
		Intersection.value.observe(itemEndEle);
	}
};
const alterData = (isFirst: boolean, direction?: "end" | "head") => {
	if (isFirst) {
		visibleSearchData.value = allSearchData.value!.slice(0, oneGroupDataNum.value);
		return;
	}
	switch (direction) {
		case "end": {
			// 未达到最大允许数据条数
			if (visibleSearchData.value.length < 3 * oneGroupDataNum.value) {
				// 清空之前的标记节点
				alterLocation(false, true);
				visibleSearchData.value = allSearchData.value!.slice(0, currentHeadIndex.value + 2 * oneGroupDataNum.value);
			} else {
				// 清空之前的标记节点
				alterLocation(true, true);
				visibleSearchData.value = [].concat(
					visibleSearchData.value.slice(oneGroupDataNum.value),
					allSearchData.value!.slice(
						currentHeadIndex.value + 2 * oneGroupDataNum.value,
						currentHeadIndex.value + 3 * oneGroupDataNum.value
					)
				);
			}
			currentHeadIndex.value += oneGroupDataNum.value;
			currentMaxIndex.value = currentHeadIndex.value + oneGroupDataNum.value;
			isUpdate = true;
			break;
		}
		case "head": {
			// 清空之前的标记节点
			alterLocation(true, true);
			visibleSearchData.value = [].concat(
				allSearchData.value!.slice(
					currentHeadIndex.value - 3 * oneGroupDataNum.value,
					currentHeadIndex.value - 2 * oneGroupDataNum.value
				),
				visibleSearchData.value.slice(0, visibleSearchData.value.length - oneGroupDataNum.value)
			);
			currentHeadIndex.value -= oneGroupDataNum.value;
		}
	}
};
// 向下滚动时增加可滚动距离
const addScrollDistance = () => {
	// 没有达到最大允许数据条数时或向上滚动不需要手动增加可滚动距离
	if (
		currentHeadIndex.value === 0 ||
		currentHeadIndex.value === oneGroupDataNum.value ||
		currentHeadIndex.value + oneGroupDataNum.value !== currentMaxIndex.value
	) {
		return;
	}
	const len = listRef.value!.childNodes.length;
	const formatListNodes = Array.from(listRef.value!.childNodes);
	const childNodes = formatListNodes.slice(len - oneGroupDataNum.value - 1, len - 1);
	let allHeight = 0;
	for (const node of childNodes) {
		allHeight += (node as HTMLDivElement).offsetHeight;
	}
	scroollPadding.value += allHeight;
	isUpdate = false;
};
onMounted(() => {
	Intersection.value = new IntersectionObserver(
		(entries) => {
			// 初始化时或头部尾部标记都能看到时
			if (
				entries.length === 2 ||
				allSearchData.value!.length <= visibleSearchData.value.length ||
				currentHeadIndex.value + oneGroupDataNum.value >= allSearchData.value!.length
			) {
				return;
			}
			const eleClassname = entries[0].target.className;
			switch (eleClassname) {
				case "item itemHead": {
					// 当头部节点可见时
					if (entries[0].isIntersecting) {
						// 没有删除上面的节点时
						if (currentHeadIndex.value === 0 || currentHeadIndex.value === oneGroupDataNum.value) {
							return;
						}
						alterData(false, "head");
					}
					break;
				}
				case "item itemEnd": {
					// 当尾部节点可见时
					if (entries[0].isIntersecting) {
						alterData(false, "end");
					}
				}
			}
		},
		{
			root: searchRef.value
			// rootMargin: "8%"
		}
	);
});
onUpdated(() => {
	if (listRef.value) {
		// 修改可见数据节点后设置首尾标记
		alterLocation(visibleSearchData.value.length === 3 * oneGroupDataNum.value, false);
		if (isUpdate) {
			addScrollDistance();
		}
	}
});
bookPrototype.value.ready.then(() => {
	isAnalysisOver.value = true;
});
// 确认搜索
const stopWatchSrarch = watch([confirmSearch, searchValue], () => {
	if (confirmSearch.value && searchValue.value.trim().length > 0) {
		if (!isSearching.value) {
			isSearching.value = true;
			doSearch(bookPrototype.value as Book, searchValue.value)
				.then((res) => {
					searchRef.value!.scrollTop = 0;
					allSearchData.value = res.map((item: any, index: number) => {
						item.excerpt =
							`<li class="searchItemNum">${index}</li>` +
							item.excerpt.replace(new RegExp(searchValue.value, "gi"), `<span class="light_height_text">$&</span>`);
						return item;
					});
					// 初始化可见数据
					alterData(true);
				})
				.then(() => {
					isSearching.value = false;
				});
		}
		confirmSearch.value = false;
	}
});

const handelClickSearchItem = throttle((cfi: string) => {
	bookPrototype.value.rendition.display(cfi).then(() => {
		currentLocationPercentage.value = getCurrentLocation(bookPrototype.value as Book, totalPageLength.value).percentage;
		currentSection.value = getCurrentLocation(bookPrototype.value as Book, totalPageLength.value).section;
	});
	showDialog.value = false;
	currentMenu.value = "";
	bookPrototype.value.rendition.annotations.highlight(cfi);
}, 1000);
onUnmounted(() => {
	stopWatchSrarch();
});
</script>

<style scoped lang="scss">
.search {
	width: 100%;
	height: calc(100% - 54px);
	overflow-y: scroll;
	overflow-x: hidden;
	box-sizing: border-box;
	position: relative;

	.list {
		.item {
			padding: 1.429rem 0.714rem 1.429rem 2.34rem;
			border-bottom: 1px solid #9b8d8d;
			box-sizing: border-box;
			position: relative;

			:deep(.light_height_text) {
				color: red;
				font-weight: 600;
			}

			:deep(.searchItemNum) {
				color: #1c7de5;
				position: absolute;
				top: 50%;
				left: 3%;
				transform: translateY(-50%);
			}
		}
	}

	.searching {
		position: absolute;
		z-index: 1001;
		font-size: 1.2rem;
		left: 50%;
		transform: translateX(-50%);
		top: 4%;
		color: red;
		white-space: nowrap;
		background-color: #d7cf96;
		padding: 0.714rem;
	}
}

/* 滚动条的整体样式 */
.search::-webkit-scrollbar {
	width: 5px;
}

/* 滚动条滑块的样式 */
.search::-webkit-scrollbar-thumb {
	background-color: #9e4fe7;
	border-radius: 3px;
}

.search::-webkit-scrollbar-thumb:hover {
	background-color: #af97f1;
}

/* 滚动条轨道的样式 */
.search::-webkit-scrollbar-track {
	background-color: transparent;
}
</style>
